#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;

#define SIZE 1024
#define MAX_SIZE 64
#define SEP " "

char* argv[MAX_SIZE];
char pwd[SIZE];
char env[SIZE]; // 要导入的环境变量
int lastcode = 0;

const char* HostName()
{
    char* hostname = getenv("HOSTNAME");
    if (hostname) 
        return hostname;
    else
        return "None";
}
const char* UserName()
{
    char* username = getenv("USER");
    if (username) 
        return username;
    else
        return "None";
}
const char* WorkDir()
{
    char* workdir = getenv("PWD");
    if (workdir)
        return workdir;
    else 
        return "None";
}

char* Home()
{
    return getenv("HOME");
}

int Interact(char* out, int size)
{
    // 输出命令行提示符并获取用户输入的命令字符串
    fprintf(stdout, "[%s@%s %s]$ ", UserName(), HostName(), WorkDir());
    fgets(out, size, stdin);
    out[strlen(out) - 1] = NULL; // fgets也会把'\n'字符存储，这里去掉此多余字符
    return strlen(out);
}

void Split(char* in)
{
    int i = 0;
    //strtok 对输入的字符串指令进行切割
    argv[i++] = strtok(in, SEP);
    while (argv[i++] = strtok(NULL, SEP))
    {   }
    // 让ls指令显示的目录和文件添加上该有的颜色
    if (strcmp(argv[0], "ls") == 0)
    {
        argv[i - 1] = (char*)"--color";  // 这里把ls指令的颜色选项功能带上
        argv[i] = NULL;
    }
}

void Execute()
{
    pid_t id = fork();
    if (id == 0)
    {
        execvp(argv[0], argv);
        exit(1);
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid == id) lastcode = WEXITSTATUS(status);
}

int BuildCommand()
{
    int ret = 0;
    //1, 检测输入的指令程序是否是内建指令，1表示是，0表示不是
    if (strcmp("cd", argv[0]) == 0)
    {
        //2, 开始执行
        ret = 1;
        char* target = argv[1];  // 这里cd内建指令可能存在选项参数
        if (!target) target = Home(); // cd无参形式的情况
        // 这里要注意类似于cd ..的情况
        chdir(target);
        char temp[1024];
        getcwd(temp, 1024);
        snprintf(pwd, SIZE, "PWD=%s", temp);
        putenv(pwd);
    }
    // 处理导入系统下的环境变量
    else if(strcmp("export", argv[0]) == 0)
    {
        ret = 1;
        if (argv[1])
        {
            strcpy(env, argv[1]);
            putenv(env);
        }
    }
    else if(strcmp("echo", argv[0]) == 0)
    {
        ret = 1;
        if (argv[1] == NULL)
        {
            cout << endl;
        }
        else 
        {
            if (argv[1][0] == '$')
            {
                if (argv[1][1] == '?')
                {
                    cout << lastcode << endl;
                    lastcode = 0;
                }
                else 
                {
                    char* e = getenv(argv[1] + 1);
                    if (e) cout << e << endl;
                }
            }
            else  
            {
                cout << argv[1] << endl;
            }
        }
    }
    return ret;
}

int main()
{
    while (true)
    {
        //1, 输出命令行提示符并获取用户输入的命令字符串                                                                                                                  
        char commandline[SIZE];                                                                                                                                           
        int n = Interact(commandline, SIZE);                                                                                                                             
        if (n == 0) continue;                                                                                                                                            
        //2, 对命令行字符串进行切割                                                                                                                                      
        Split(commandline);                                                                                                                                                            
        //3, 处理内建命令 
        n = BuildCommand();                                                                                                                                                            
        if (n) continue;                                                                                                                                                               
        //4, 开始执行输入的程序指令                                                                                                                                      
        Execute();
    }
    return 0;
}
